﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Threading;

namespace ICommonLibrary.Collection {

    public class ReadWriteHashSet<T> : ICollection<T> {

        private readonly HashSet<T> _set = new HashSet<T>();
        private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();

        public IEqualityComparer<T> Comparer {
            get { return _set.Comparer; }
        }

        public void Clear() {
            _lock.EnterWriteLock();
            try {
                _set.Clear();
            }
            finally {
                _lock.ExitWriteLock();
            }
        }

        public bool Contains(T item) {
            _lock.EnterReadLock();
            try {
                return _set.Contains(item);
            }
            finally {
                _lock.ExitReadLock();
            }
        }

        public void CopyTo(T[] array, int arrayIndex) {
            _lock.EnterReadLock();
            try {
                _set.CopyTo(array, arrayIndex);
            }
            finally {
                _lock.ExitReadLock();
            }
        }

        public bool Remove(T item) {
            _lock.EnterWriteLock();
            try {
                return _set.Remove(item);
            }
            finally {
                _lock.ExitWriteLock();
            }
        }

        public IEnumerator GetEnumerator() {
            _lock.EnterReadLock();
            try {
                return ((IEnumerable)new List<T>(_set)).GetEnumerator();
            }
            finally {
                _lock.ExitReadLock();
            }
        }

        IEnumerator<T> IEnumerable<T>.GetEnumerator() {
            _lock.EnterReadLock();
            try {
                return new List<T>(_set).GetEnumerator();
            }
            finally {
                _lock.ExitReadLock();
            }
        }

        public void Add(T item) {
            _lock.EnterWriteLock();
            try {
                _set.Add(item);
            }
            finally {
                _lock.ExitWriteLock();
            }
        }

        public int Count {
            get {
                _lock.EnterReadLock();
                try {
                    return _set.Count;
                }
                finally {
                    _lock.ExitReadLock();
                }
            }
        }

        public bool IsReadOnly {
            get { return false; }
        }

        public void GetObjectData(SerializationInfo info, StreamingContext context) {
            _lock.EnterReadLock();
            try {
                _set.GetObjectData(info, context);
            }
            finally {
                _lock.ExitReadLock();
            }
        }

        public void OnDeserialization(object sender) {
            _lock.EnterWriteLock();
            try {
                _set.OnDeserialization(sender);
            }
            finally {
                _lock.ExitWriteLock();
            }
        }

        public void UnionWith(IEnumerable<T> other) {
            _lock.EnterWriteLock();
            try {
                _set.UnionWith(other);
            }
            finally {
                _lock.ExitWriteLock();
            }
        }

        public void IntersectWith(IEnumerable<T> other) {
            _lock.EnterWriteLock();
            try {
                _set.IntersectWith(other);
            }
            finally {
                _lock.ExitWriteLock();
            }
        }

        public void ExceptWith(IEnumerable<T> other) {
            _lock.EnterWriteLock();
            try {
                _set.ExceptWith(other);
            }
            finally {
                _lock.ExitWriteLock();
            }
        }

        public void SymmetricExceptWith(IEnumerable<T> other) {
            _lock.EnterWriteLock();
            try {
                _set.SymmetricExceptWith(other);
            }
            finally {
                _lock.ExitWriteLock();
            }
        }

        public bool IsSubsetOf(IEnumerable<T> other) {
            _lock.EnterReadLock();
            try {
                return _set.IsSubsetOf(other);
            }
            finally {
                _lock.ExitReadLock();
            }
        }

        public bool IsProperSubsetOf(IEnumerable<T> other) {
            _lock.EnterReadLock();
            try {
                return _set.IsProperSubsetOf(other);
            }
            finally {
                _lock.ExitReadLock();
            }
        }

        public bool IsSupersetOf(IEnumerable<T> other) {
            _lock.EnterReadLock();
            try {
                return _set.IsSupersetOf(other);
            }
            finally {
                _lock.ExitReadLock();
            }
        }

        public bool IsProperSupersetOf(IEnumerable<T> other) {
            _lock.EnterReadLock();
            try {
                return _set.IsProperSupersetOf(other);
            }
            finally {
                _lock.ExitReadLock();
            }
        }

        public bool Overlaps(IEnumerable<T> other) {
            _lock.EnterReadLock();
            try {
                return _set.Overlaps(other);
            }
            finally {
                _lock.ExitReadLock();
            }
        }

        public bool SetEquals(IEnumerable<T> other) {
            _lock.EnterReadLock();
            try {
                return _set.SetEquals(other);
            }
            finally {
                _lock.ExitReadLock();
            }
        }

        public void CopyTo(T[] array) {
            _lock.EnterReadLock();
            try {
                _set.CopyTo(array);
            }
            finally {
                _lock.ExitReadLock();
            }
        }

        public void CopyTo(T[] array, int arrayIndex, int count) {
            _lock.EnterReadLock();
            try {
                _set.CopyTo(array, arrayIndex, count);
            }
            finally {
                _lock.ExitReadLock();
            }
        }

        public int RemoveWhere(Predicate<T> match) {
            _lock.EnterWriteLock();
            try {
                return _set.RemoveWhere(match);
            }
            finally {
                _lock.ExitWriteLock();
            }
        }

        public void TrimExcess() {
            _lock.EnterWriteLock();
            try {
                _set.TrimExcess();
            }
            finally {
                _lock.ExitWriteLock();
            }
        }
    }
}